/*
Minims .Net Playstation 3 UPNP ContentDirectory
Copyright (C) 2008  Guillaume Plourde

This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; version 2
of the License.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
*/

using System;
using System.Collections.Generic;
using System.Text;
using System.IO;
using System.Xml;

namespace minims
{
	public class CDS_ITEM
	{
		public string classname;
	}
	public class MediaItem : CDS_ITEM
	{
		public int id;
		public string title;
		public string creator;
		public string filename;
		public long size;
		public string protocol;
		public bool restricted;

		public MediaItem(string _title, string _creator, string _filename, long _size, string _protocol, int _id)
		{
			this.title = _title;
			this.creator = _creator;
			this.filename = _filename;
			this.size = _size;
			this.id = _id;
			this.protocol = _protocol;

			base.classname = "media";
		}
	}
	public class Container : CDS_ITEM
	{
		public List<CDS_ITEM> items;

		int id;
		int parentid;
		string cdsclass;
		string title;

		public Container(string _title, string _cdsclass, int _id, int _parentid)
		{

			this.title = _title;
			this.id = _id;
			this.parentid = _parentid;
			this.cdsclass = _cdsclass;

			items = new List<CDS_ITEM>();

			base.classname = "container";
		}

		public void AddContainer(string title, string cdsclass, int i)
		{
			Container container = new Container(title, cdsclass, i, this.id);
			items.Add(container);
		}

		public string GetTitle
		{
			get
			{
				return this.title;
			}
		}
		public int GetIndex
		{
			get
			{
				return this.id;
			}
		}
		public int GetParentIndex
		{
			get
			{
				return this.parentid;
			}
		}
		public int GetNumChilds
		{
			get
			{
				return this.items.Count;
			}
		}
	}
	public class ContentDirectory : Service
	{
		int id;

		public Container root;

		private void GetContainers(Container parent, string dir)
		{
			parent.AddContainer(dir, "object.container", id++);

			// fill items
			Container container = (Container)parent.items[parent.items.Count - 1];
			String[] files = Directory.GetFiles(dir);

			foreach (string f in files)
			{
				FileInfo fi = new FileInfo(f);
				if (fi.Extension != ".divx" && fi.Extension != ".wmv" && fi.Extension != ".mpeg" && fi.Extension != ".mpg")
					continue;
				
				MediaItem item;

				if (fi.Extension == ".divx")
				{
 					item = new MediaItem(fi.Name, "unknown author", fi.FullName, 0, "http-get:*:video/x-divx:DLNA.ORG_OP=01;DLNA.ORG_CI=0;DLNA.ORG_FLAGS=01500000000000000000000000000000", id); //http-get:*:video/x-ms-wmv:DLNA.ORG_PN=WMVMED_BASE;DLNA.ORG_OP=01;DLNA.ORG_CI=0;DLNA.ORG_FLAGS=01500000000000000000000000000000 //http-get:*:video/x-divx:DLNA.ORG_OP=01;DLNA.ORG_CI=0;DLNA.ORG_FLAGS=01500000000000000000000000000000
					id++;
					container.items.Add(item);
				}
				else if (fi.Extension == ".wmv")
				{
					item = new MediaItem(fi.Name, "unknown author", fi.FullName, 0, "http-get:*:video/x-ms-wmv:DLNA.ORG_OP=01;DLNA.ORG_CI=0;DLNA.ORG_FLAGS=01500000000000000000000000000000", id);
					id++;
					container.items.Add(item);
				}
				else if (fi.Extension == ".mpeg" || fi.Extension == ".mpg")
				{
					item = new MediaItem(fi.Name, "unknown author", fi.FullName, 0, "http-get:*:video/mpeg:*", id);
					id++;
					container.items.Add(item);
				}
				//else
				//{
				//    // run mencoder transcoding on real player and avi files
				//    // Need to add a layer of detection for xvid and avi files here.
				//}
			}

			// Recurse to next directory
			foreach (string strDirName in Directory.GetDirectories(dir))
			{
				GetContainers(container, strDirName);
			}
		}
		public Container FindContainer(Container c, int n)
		{
			if (c.GetIndex == n)
				return c;

			foreach (CDS_ITEM cc in c.items)
			{
				if (cc.classname == "container")
				{
					Container container = (Container)cc;
					Container temp = FindContainer(container, n);
					if (temp != null)
						return temp;
				}
			}
			return null;
		}
		public void CollectFilesFolders(string path) // types 0: video, 1: music, 2: photo
		{
			GetContainers((Container)root.items[0], path);
		}
		public ContentDirectory(List<string> folders) : base ("ContentDirectory")
		{
			id = 0;
			//containers = new List<Container>();

			// Create Root
			root = new Container("Root", "object.container", 0, -1);
			id++;

			// Create Childs
			root.AddContainer("Video", "object.container", id++);
			root.AddContainer("Music", "object.container", id++);
			root.AddContainer("Photo", "object.container", id++);

			// Collect video containers
			foreach (string folder in folders)
			{
				CollectFilesFolders(folder);
			}
			// GetContainers((Container)root.items[0], @"g:\media\");
		}

		private String BuildHTTPMessage(string func, string schemas, string body)
		{
			StringBuilder sb = new StringBuilder();

			sb.Append("<?xml version=\"1.0\" encoding=\"utf-8\"?>");
			sb.Append("<s:Envelope s:encodingStyle=\"http://schemas.xmlsoap.org/soap/encoding/\" xmlns:s=\"http://schemas.xmlsoap.org/soap/envelope/\">");
			sb.Append("<s:Body>");
			sb.Append("<u:" + func + "Response xmlns:u=\"" + schemas + "\">");
			sb.Append(body);
			sb.Append("</u:" + func + "Response>");
			sb.Append("</s:Body>");
			sb.Append("</s:Envelope>");

			return sb.ToString();
		}
		public String GetSearchCapabilities(string schemas)
		{
			Console.WriteLine("CDS: GetSearchCapabilities...");
			String response = "<SearchCaps>dc:title,dc:creator,upnp:class,upnp:album,res@protocolInfo,res@size,res@bitrate</SearchCaps>";
			return BuildHTTPMessage("GetSearchCapabilities", schemas, response);
		}
		public String GetSortCapabilities(string schemas)
		{
			Console.WriteLine("CDS: GetSortCapabilities...");
			String response = "<SortCaps>dc:title,dc:creator,upnp:class,upnp:album</SortCaps>";
			return BuildHTTPMessage("GetSortCapabilities", schemas, response);
		}
		public String GetSystemUpdateID(string schemas)
		{
			Console.WriteLine("CDS: GetSystemUpdateID...");

			String response = "<Id>" + GenerateUpdateID() + "</Id>";
			return BuildHTTPMessage("GetSystemUpdateID", schemas, response);
		}
		public String Browse(int ObjectID, string BrowserFlag, string Filter, int StartIndex, int RequestedCount, string SortCriteria, string schemas)
		{
			int i, c;

			Container container = FindContainer(root, ObjectID);
			StringBuilder sb = new StringBuilder();
			sb.Append("<Result>");
			sb.Append("&lt;DIDL-Lite xmlns=\"urn:schemas-upnp-org:metadata-1-0/DIDL-Lite/\" xmlns:dc=\"http://purl.org/dc/elements/1.1/\" xmlns:upnp=\"urn:schemas-upnp-org:metadata-1-0/upnp/\"&gt;");

			if (BrowserFlag == "BrowseMetadata")
			{
				sb.Append("&lt;container id=\"" + container.GetIndex + "\" parentID=\"" + container.GetParentIndex + "\" searchable=\"0\" restricted=\"1\" childcount=\"" + container.GetNumChilds + "\"&gt;");
				sb.Append("&lt;dc:title&gt;" + container.GetTitle + "&lt;/dc:title&gt;");
				sb.Append("&lt;upnp:class&gt;object.container&lt;/upnp:class&gt;");
				sb.Append("&lt;/container&gt;");
				sb.Append("&lt;/DIDL-Lite&gt;");
				sb.Append("</Result>");
				sb.Append("<NumberReturned>1</NumberReturned>");
				sb.Append("<TotalMatches>1</TotalMatches>");
				sb.Append("<UpdateID>" + GenerateUpdateID() + "</UpdateID>");
			}
			else
			{
				c = 0;

				//for (i = StartIndex; i < (RequestedCount + StartIndex); i++)
				for (i = StartIndex; i < 100; i++)
				{
					if (i > container.items.Count - 1)
						continue;

					if (container.items[i].classname == "container")
					{
						Container cn = (Container)container.items[i];

						sb.Append("&lt;container id=\"" + cn.GetIndex + "\" parentID=\"" + cn.GetParentIndex + "\" searchable=\"0\" restricted=\"1\" childCount=\"" + cn.GetNumChilds + "\"&gt;");
						sb.Append("&lt;dc:title&gt;" + cn.GetTitle + "&lt;/dc:title&gt;");
						sb.Append("&lt;upnp:class&gt;object.container&lt;/upnp:class&gt;");
						//sb.Append("&lt;upnp:writeStatus&gt;UNKNOWN&lt;/upnp:writeStatus&gt;");
						sb.Append("&lt;/container&gt;");
						c++;
					}
					else
					{
						MediaItem media = (MediaItem)container.items[i];

						sb.Append("&lt;item id=\"" + media.id + "\" parentID=\"" + container.GetIndex + "\" restricted=\"false\"&gt;");
						sb.Append("&lt;dc:title&gt;" + media.title + "&lt;/dc:title&gt;");
						sb.Append("&lt;dc:creator&gt;" + media.creator + "&lt;/dc:creator&gt;");
						sb.Append("&lt;upnp:class&gt;object.item.videoItem&lt;/upnp:class&gt;");
						sb.Append("&lt;res protocolInfo=\"" + media.protocol + "\"&gt;");
						sb.Append("http://" + ConfigManager.GetHostIp + ":" + ConfigManager.GetPort + "/Media/" + container.GetIndex + "/" + media.id);
						sb.Append("&lt;/res&gt;");
						sb.Append("&lt;/item&gt;");
						c++;
					}
				}

				sb.Append("&lt;/DIDL-Lite&gt;");
				sb.Append("</Result>");
				sb.Append("<NumberReturned>" + c + "</NumberReturned>");
				sb.Append("<TotalMatches>" + c + "</TotalMatches>");
				sb.Append("<UpdateID>" + GenerateUpdateID() + "</UpdateID>");
			}
			return BuildHTTPMessage("Browse", schemas, sb.ToString());
		}
		public string Search()
		{
			return "";
		}

		private int upid = 0;
		private int GenerateUpdateID()
		{
			return upid;
		}

		#region Service Description XML, use GetDescription()
		static public string GetDescription()
		{
			// Edit this xml to match device capabilities
			StringBuilder desc = new StringBuilder();
			desc.Append("<?xml version=\"1.0\" encoding=\"utf-8\"?>");
			desc.Append("<scpd xmlns=\"urn:schemas-upnp-org:service-1-0\"><specVersion><major>1</major><minor>0</minor></specVersion>");
			desc.Append("<actionList>");
			desc.Append("<action><name>GetSearchCapabilities</name><argumentList><argument><name>SearchCaps</name><direction>out</direction><relatedStateVariable>SearchCapabilities</relatedStateVariable></argument></argumentList></action>");
			desc.Append("<action><name>GetSortCapabilities</name><argumentList><argument><name>SortCaps</name><direction>out</direction><relatedStateVariable>SortCapabilities</relatedStateVariable></argument></argumentList></action>");
			desc.Append("<action><name>GetSystemUpdateID</name><argumentList><argument><name>Id</name><direction>out</direction><relatedStateVariable>SystemUpdateID</relatedStateVariable></argument></argumentList></action>");
			desc.Append("<action><name>Browse</name><argumentList><argument><name>ObjectID</name><direction>in</direction><relatedStateVariable>A_ARG_TYPE_ObjectID</relatedStateVariable></argument><argument><name>BrowseFlag</name><direction>in</direction><relatedStateVariable>A_ARG_TYPE_BrowseFlag</relatedStateVariable></argument><argument><name>Filter</name><direction>in</direction><relatedStateVariable>A_ARG_TYPE_Filter</relatedStateVariable></argument><argument><name>StartingIndex</name><direction>in</direction><relatedStateVariable>A_ARG_TYPE_Index</relatedStateVariable></argument><argument><name>RequestedCount</name><direction>in</direction><relatedStateVariable>A_ARG_TYPE_Count</relatedStateVariable></argument><argument><name>SortCriteria</name><direction>in</direction><relatedStateVariable>A_ARG_TYPE_SortCriteria</relatedStateVariable></argument><argument><name>Result</name><direction>out</direction><relatedStateVariable>A_ARG_TYPE_Result</relatedStateVariable></argument><argument><name>NumberReturned</name><direction>out</direction><relatedStateVariable>A_ARG_TYPE_Count</relatedStateVariable></argument><argument><name>TotalMatches</name><direction>out</direction><relatedStateVariable>A_ARG_TYPE_Count</relatedStateVariable></argument><argument><name>UpdateID</name><direction>out</direction><relatedStateVariable>A_ARG_TYPE_UpdateID</relatedStateVariable></argument></argumentList></action>");
			desc.Append("</actionList>");
			desc.Append("<serviceStateTable>");
			desc.Append("<stateVariable sendEvents=\"no\"><name>A_ARG_TYPE_ObjectID</name><dataType>string</dataType></stateVariable>");
			desc.Append("<stateVariable sendEvents=\"no\"><name>A_ARG_TYPE_Result</name><dataType>string</dataType></stateVariable>");
			desc.Append("<stateVariable sendEvents=\"no\"><name>A_ARG_TYPE_SearchCriteria</name><dataType>string</dataType></stateVariable>");
			desc.Append("<stateVariable sendEvents=\"no\"><name>A_ARG_TYPE_BrowseFlag</name><dataType>string</dataType><allowedValueList><allowedValue>BrowseMetadata</allowedValue><allowedValue>BrowseDirectChildren</allowedValue></allowedValueList></stateVariable>");
			desc.Append("<stateVariable sendEvents=\"no\"><name>A_ARG_TYPE_Filter</name><dataType>string</dataType></stateVariable>");
			desc.Append("<stateVariable sendEvents=\"no\"><name>A_ARG_TYPE_SortCriteria</name><dataType>string</dataType></stateVariable>");
			desc.Append("<stateVariable sendEvents=\"no\"><name>A_ARG_TYPE_Index</name><dataType>ui4</dataType></stateVariable>");
			desc.Append("<stateVariable sendEvents=\"no\"><name>A_ARG_TYPE_Count</name><dataType>ui4</dataType></stateVariable>");
			desc.Append("<stateVariable sendEvents=\"no\"><name>A_ARG_TYPE_UpdateID</name><dataType>ui4</dataType></stateVariable>");
			desc.Append("<stateVariable sendEvents=\"no\"><name>SearchCapabilities</name><dataType>string</dataType></stateVariable>");
			desc.Append("<stateVariable sendEvents=\"no\"><name>SortCapabilities</name><dataType>string</dataType></stateVariable>");
			desc.Append("<stateVariable sendEvents=\"yes\"><name>SystemUpdateID</name><dataType>ui4</dataType></stateVariable>");
			desc.Append("<stateVariable sendEvents=\"yes\"><name>ContainerUpdateIDs</name><dataType>string</dataType></stateVariable>");
			desc.Append("</serviceStateTable></scpd>");
			return desc.ToString();
		}
		#endregion
	}
}
